/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "XComponent.h"
#include "GPURender.h"
#include "napi_tool.h"

std::unordered_map<std::string, XComponent *> XComponent::instance_;
OH_NativeXComponent_Callback XComponent::callback_;

static void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window)
{
    int32_t ret;
    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        return;
    }

    std::string id(idStr);
    auto instance = XComponent::GetInstance(id);
    instance->OnSurfaceCreated(component, window);
}

static void OnSurfaceChangedCB(OH_NativeXComponent *component, void *window)
{
    int32_t ret;
    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        return;
    }

    std::string id(idStr);
    auto instance = XComponent::GetInstance(id);
    instance->OnSurfaceChanged(component, window);
}

static void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window)
{
    int32_t ret;
    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        return;
    }

    std::string id(idStr);
    auto instance = XComponent::GetInstance(id);
    instance->OnSurfaceDestroyed(component, window);
}

XComponent::XComponent(const std::string &id)
{
    id_ = id;
    component_ = nullptr;
    auto XComponent = XComponent::GetNXComponentCallback();
    XComponent->OnSurfaceCreated = OnSurfaceCreatedCB;
    XComponent->OnSurfaceChanged = OnSurfaceChangedCB;
    XComponent->OnSurfaceDestroyed = OnSurfaceDestroyedCB;
}

XComponent *XComponent::GetInstance(const std::string &id)
{
    if (instance_.find(id) == instance_.end()) {
        XComponent *instance = new XComponent(id);
        instance_[id] = instance;
        return instance;
    } else {
        return instance_[id];
    }
}

OH_NativeXComponent_Callback *XComponent::GetNXComponentCallback()
{
    return &XComponent::callback_;
}

void XComponent::SetNativeXComponent(OH_NativeXComponent *component)
{
    component_ = component;
    OH_NativeXComponent_RegisterCallback(component_, &XComponent::callback_);
}

void XComponent::OnSurfaceCreated(OH_NativeXComponent *component,
                                  void *window)
{
    window_ = window;
    int32_t ret = OH_NativeXComponent_GetXComponentSize(component, window,
                                                        &width_, &height_);
    if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        GPURender::getInstance()->startRenderThread(window, width_, height_);
    } else {
        LOGE("XComponent::OnSurfaceCreated failed");
    }
}

void XComponent::OnSurfaceChanged(OH_NativeXComponent *component,
                                  void *window)
{
    window_ = window;
    int32_t ret = OH_NativeXComponent_GetXComponentSize(component, window,
                                                        &width_, &height_);
    if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        LOGE("XComponent::OnSurfaceChanged failed");
    }
}

void XComponent::OnSurfaceDestroyed(OH_NativeXComponent *component,
                                    void *window) {}

napi_value XComponent::GetXComponentId(napi_env env, napi_callback_info info)
{
    napi_value thisArg;
    napi_status status;
    napi_value exportInstance;
    OH_NativeXComponent *nativeXComponent = nullptr;

    int32_t ret;
    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;

    NAPI_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &thisArg, NULL));
    status = napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ,
                                     &exportInstance);
    status = napi_unwrap(env, exportInstance,
                         reinterpret_cast<void **>(&nativeXComponent));
    ret = OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize);
    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        return nullptr;
    }

    std::string id(idStr);

    napi_value output;
    NAPI_CALL(env, napi_create_string_utf8(env, idStr, id.length(), &output));

    return output;
}